package com.stress.testing.instrumentation;

import com.stress.testing.common.interceptor.Interceptor;
import com.stress.testing.common.interceptor.InvokeResult;
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
import net.bytebuddy.implementation.bind.annotation.Super;
import net.bytebuddy.implementation.bind.annotation.SuperCall;
import net.bytebuddy.implementation.bind.annotation.This;

import java.lang.reflect.Method;
import java.util.concurrent.Callable;

public class SimpleMethodDelegation {
    private Interceptor interceptor;

    public SimpleMethodDelegation(Interceptor interceptor) {
        this.interceptor = interceptor;
    }

    @RuntimeType
    public Object intercept(@Origin Method method,
                            @AllArguments Object[] args,
                            @SuperCall Callable<?> superCall,
                            @Super Object zuper,
                            @This Object thiz
    ) throws Exception {
        Object result = null;
        Exception e = null;

        try {
            interceptor.before(method, args, zuper, thiz);

            InvokeResult invokeResult = interceptor.invoke(method, args, zuper, thiz);
            if (invokeResult.isInvoked()) {
                result = invokeResult.getResult();
            } else {
                result = superCall.call();
            }

            result = interceptor.after(result, args, zuper, thiz);
        } catch (Exception ex) {
            e = ex;
            Exception exception = interceptor.exception(ex);
            if (exception != null) {
                throw exception;
            }
        } finally {
            interceptor.afterReturning(result, e);
        }

        return result;
    }
}
